/*
 * @Author: crissu 13750782397@163.com
 * @Date: 2024-05-29 01:45:34
 * @LastEditors: crissu 13750782397@163.com
 * @LastEditTime: 2024-05-29 01:52:33
 * @FilePath: /try_concurrence/MemoryAccessSynchronization/tryCond1.go
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
package memoryaccesssynchronization

import (
	"fmt"
	"sync"
	"time"
)

type Button struct {
	Clicked *sync.Cond
}

func TryCond1() {
	button := Button{
		Clicked: sync.NewCond(&sync.Mutex{}),
	}

	// running on goroutine every function that passed/registered
	// and wait, not exit until that goroutine is confirmed to be running
	subscribe := func(c *sync.Cond, param string, fn func(s string)) {
		var goroutineRunning sync.WaitGroup
		goroutineRunning.Add(1)

		go func(p string) {
			goroutineRunning.Done()
			c.L.Lock() // critical section
			defer c.L.Unlock()

			fmt.Println("Registered and wait ... ")
			c.Wait()

			fn(p)
		}(param)

		goroutineRunning.Wait()
	}

	var clickRegistered sync.WaitGroup

	for _, v := range []string{
		"Maximizing window.",
		"Displaying annoying dialog box!",
		"Mouse clicked."} {

		clickRegistered.Add(1)

		subscribe(button.Clicked, v, func(s string) {
			fmt.Println(s)
			clickRegistered.Done()
		})
	}

	button.Clicked.Broadcast()

	clickRegistered.Wait()
}

// a goroutine that is waiting for a signal, and a goroutine that is sending signals.
// Say we have a queue of fixed length 2, and 10 items we want to push onto the queue
func TryCond2() {
	c := sync.NewCond(&sync.Mutex{})
	queue := make([]interface{}, 0, 10)

	removeFromQueue := func(delay time.Duration) {
		time.Sleep(delay)
		c.L.Lock() // critical section

		queue = queue[1:]

		fmt.Println("Removed from queue")

		c.L.Unlock()
		c.Signal() // let a goroutine waiting on the condition know that something has ocurred
	}

	for i := 0; i < 10; i++ {
		c.L.Lock() // critical section

		// When the queue is equal to two the main goroutine is suspend
		// until a signal on the condition has been sent
		for len(queue) == 2 {
			c.Wait()
		}

		fmt.Println("Adding to queue")
		queue = append(queue, struct{}{})

		go removeFromQueue(1 * time.Second)

		c.L.Unlock()
	}
}
